package org.apache.ocean.server;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;

import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.highlight.Formatter;
import org.apache.lucene.search.highlight.Fragmenter;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.NullFragmenter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.apache.lucene.search.highlight.TextFragment;
import org.apache.ocean.ClusterConstants;
import org.apache.ocean.HighlightRequest;
import org.apache.ocean.HighlightResponse;
import org.apache.ocean.IndexSchema;
import org.apache.ocean.OceanDocument;

/**
 * Collection of Utility and Factory methods for Highlighting.
 */
public class Highlightings {
  private static final String SIMPLE = "simple";
  private static final String HIGHLIGHT = "hl";
  private static final String PREFIX = "hl.";
  private static final String FIELDS = PREFIX + "fl";
  private static final String SNIPPETS = PREFIX + "snippets";
  private static final String FRAGSIZE = PREFIX + "fragsize";
  private static final String FORMATTER = PREFIX + "formatter";
  private static final String SIMPLE_PRE = PREFIX + SIMPLE + ".pre";
  private static final String SIMPLE_POST = PREFIX + SIMPLE + ".post";
  private static final String FIELD_MATCH = PREFIX + "requireFieldMatch";
  public int snippets = 1;
  public int fragsize = 100;
  public String formatter = SIMPLE;
  public String simplePre = "<em>";
  public String simplePost = "</em>";
  public SortedSet<String> fields;
  public IndexSchema indexSchema;

  public Highlightings(HighlightRequest highlightRequest, IndexSchema indexSchema) {
    this.fields = highlightRequest.highlightFields;
    this.indexSchema = indexSchema;
  }

  /**
   * Return a Highlighter appropriate for this field.
   * @param query The current Query
   * @param fieldName The name of the field
   */
  public Highlighter getHighlighter(Query query, String fieldName) {
    Formatter formatter = getFormatter(fieldName);
    QueryScorer queryScorer = getQueryScorer(query, fieldName);
    Highlighter highlighter = new Highlighter(formatter, queryScorer);
    Fragmenter fragmenter = getFragmenter(fieldName);
    highlighter.setTextFragmenter(fragmenter);
    return highlighter;
  }

  /**
   * Return a QueryScorer suitable for this Query and field.
   * @param query The current query
   * @param fieldName The name of the field
   * @param request The SolrQueryRequest
   */
  public static QueryScorer getQueryScorer(Query query, String fieldName) {
    //if (reqFieldMatch) {
    //  return new QueryScorer(query, request.getSearcher().getReader(), fieldName);
    //}
    //else {
    return new QueryScorer(query);
  //}
  }

  private static boolean emptyArray(String[] arr) {
    return (arr == null || arr.length == 0 || arr[0] == null || arr[0].trim().length() == 0);
  }

  /**
   * Return a formatter appropriate for this field. If a formatter
   * has not been configured for this field, fall back to the configured
   * default or the solr default (SimpleHTMLFormatter).
   *
   * @param fieldName The name of the field
   * @param request The current SolrQueryRequest
   * @return An appropriate Formatter.
   */
  public Formatter getFormatter(String fieldName) {
    // SimpleHTMLFormatter is the only supported Formatter at the moment
    return new SimpleHTMLFormatter(simplePre, simplePost);
  }

  /**
   * Return a fragmenter appropriate for this field. If a fragmenter
   * has not been configured for this field, fall back to the configured
   * default or the solr default (GapFragmenter).
   *
   * @param fieldName The name of the field
   * @param request The current SolrQueryRequest
   * @return An appropriate Fragmenter.
   */
  public Fragmenter getFragmenter(String fieldName) {
    return (fragsize <= 0) ? new NullFragmenter() : new GapFragmenter(fragsize);
  }

  /**
   * Generates a list of Highlighted query fragments for each item in a list
   * of documents, or returns null if highlighting is disabled.
   *
   * @param docs query results
   * @param query the query
   * @param req the current request
   * @param defaultFields default list of fields to summarize
   *
   * @return NamedList containing a NamedList for each document, which in
   * turns contains sets (field, summary) pairs.
   */
  /**
  public HighlightResponse doHighlighting(List<CScoreDocument> documents, Query query) throws IOException {
    HighlightResponse highlightResponse = new HighlightResponse();
    for (CScoreDocument scoreDocument : documents) {
      CLong id = ClusterUtil.getID(scoreDocument.document, indexSchema);
      String shardID = ClusterUtil.getShardID(scoreDocument.document);
      HighlightResponse.DocumentHighlight documentHighlight = new HighlightResponse.DocumentHighlight(id.toString(), shardID);
      highlightResponse.add(documentHighlight);
      for (String fieldName : fields) {
        fieldName = fieldName.trim();
        String[] docTexts = scoreDocument.document.getValues(fieldName);
        if (docTexts == null) {
          continue;
        }
        Highlighter highlighter = getHighlighter(query, fieldName);
        int numFragments = snippets;
        TextFragment[] frag;
        if (docTexts.length == 1) {
          // single-valued field
          TokenStream tstream;
          try {
            // attempt term vectors
            IndexReader indexReader = scoreDocument.scoreDoc.indexReader;
            tstream = TokenSources.getTokenStream(indexReader, scoreDocument.scoreDoc.doc, fieldName);
          } catch (IllegalArgumentException e) {
            // fall back to analyzer
            tstream = new TokenOrderingFilter(indexSchema.getAnalyzer().tokenStream(fieldName, new StringReader(docTexts[0])), 10);
          }
          frag = highlighter.getBestTextFragments(tstream, docTexts[0], false, numFragments);
        } else {
          // multi-valued field
          MultiValueTokenStream tstream = new MultiValueTokenStream(fieldName, docTexts, indexSchema.getAnalyzer(), true);
          frag = highlighter.getBestTextFragments(tstream, tstream.asSingleValue(), false, numFragments);
        }
        // convert fragments back into text
        // TODO: we can include score and position information in output as snippet attributes
        if (frag.length > 0) {
          ArrayList<String> fragTexts = new ArrayList<String>();
          for (int j = 0; j < frag.length; j++) {
            if ((frag[j] != null) && (frag[j].getScore() > 0)) {
              fragTexts.add(frag[j].toString());
            }
          }
          if (fragTexts.size() > 0) {
          	for (String fragText : fragTexts) {
              documentHighlight.add(fieldName, fragText);
          	}
          }
        }
      }
    }
    return highlightResponse;
  }
  **/
  public HighlightResponse doHighlighting(List<OceanDocument> documents, Query query) throws IOException {
    HighlightResponse highlightResponse = new HighlightResponse();
    for (OceanDocument document : documents) {
      String guid = (String)document.get(ClusterConstants.GUID_FIELD);
      HighlightResponse.DocumentHighlight documentHighlight = new HighlightResponse.DocumentHighlight(guid);
      highlightResponse.add(documentHighlight);
      for (String fieldName : fields) {
        //String[] docTexts = scoreDocument.document.getValues(fieldName);
        List<Object> values = document.getValues(fieldName);
        if (values == null) continue;
        String[] docTexts = new String[values.size()];
        for (int x=0; x < values.size(); x++) {
        	docTexts[x] = (String)values.get(x);
        }
        Highlighter highlighter = getHighlighter(query, fieldName);
        int numFragments = snippets;
        TextFragment[] frag;
        if (docTexts.length == 1) {
          // single-valued field
          //TokenStream tstream;
          //try {
            // attempt term vectors
          //  IndexReader indexReader = scoreDocument.scoreDoc.indexReader;
          //  tstream = TokenSources.getTokenStream(indexReader, scoreDocument.scoreDoc.doc, fieldName);
          //} catch (IllegalArgumentException e) {
            // fall back to analyzer
          TokenStream tstream = new TokenOrderingFilter(indexSchema.getAnalyzer().tokenStream(fieldName, new StringReader(docTexts[0])), 10);
          //}
          frag = highlighter.getBestTextFragments(tstream, docTexts[0], false, numFragments);
        } else {
          // multi-valued field
          MultiValueTokenStream tstream = new MultiValueTokenStream(fieldName, docTexts, indexSchema.getAnalyzer(), true);
          frag = highlighter.getBestTextFragments(tstream, tstream.asSingleValue(), false, numFragments);
        }
        // convert fragments back into text
        // TODO: we can include score and position information in output as snippet attributes
        if (frag.length > 0) {
          ArrayList<String> fragTexts = new ArrayList<String>();
          for (int j = 0; j < frag.length; j++) {
            if ((frag[j] != null) && (frag[j].getScore() > 0)) {
              fragTexts.add(frag[j].toString());
            }
          }
          if (fragTexts.size() > 0) {
          	for (String fragText : fragTexts) {
              documentHighlight.add(fieldName, fragText);
          	}
          }
        }
      }
    }
    return highlightResponse;
  }
}
